home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir41 / tsrsrc35.zip / MARKNET.PAS < prev    next >
Pascal/Delphi Source File  |  1993-10-21  |  21KB  |  738 lines

  1. {**************************************************************************
  2. *   MARKNET - stores system information in a file for later restoration.  *
  3. *   Copyright (c) 1986,1993 Kim Kokkonen, TurboPower Software.            *
  4. *   May be freely distributed and used but not sold except by permission. *
  5. ***************************************************************************
  6. *   Version 2.7 3/4/89                                                    *
  7. *     first public release                                                *
  8. *     (based on FMARK 2.6)                                                *
  9. *   Version 2.8 3/10/89                                                   *
  10. *     store the DOS environment                                           *
  11. *     store information about the async ports                             *
  12. *   Version 2.9 5/4/89                                                    *
  13. *     for consistency                                                     *
  14. *   Version 3.0 7/21/91                                                   *
  15. *     for compatibility with DOS 5                                        *
  16. *     add Quiet option                                                    *
  17. *     save BIOS LPT port data areas                                       *
  18. *     save XMS allocation                                                 *
  19. *     add code for tracking high memory                                   *
  20. *   Version 3.1 11/4/91                                                   *
  21. *     no change                                                           *
  22. *   Version 3.2 11/22/91                                                  *
  23. *     change method of accessing high memory                              *
  24. *     store parent's length as well as segment                            *
  25. *   Version 3.3 1/8/92                                                    *
  26. *     new features for parsing and getting command line options           *
  27. *   Version 3.4 2/14/92                                                   *
  28. *     increase heap space to allow bigger FILES=                          *
  29. *     improve error reporting when out of heap space                      *
  30. *     store HMA status                                                    *
  31. *   Version 3.5 10/18/93                                                  *
  32. *     accept DOS 6                                                        *
  33. *     save BIOS com port addresses at $40:$0                              *
  34. *     store info about MSCDEX CD-ROM drives                               *
  35. ***************************************************************************
  36. *   Telephone: 719-260-6641, CompuServe: 76004,2611.                      *
  37. *   Requires Turbo Pascal 6 or 7 to compile.                              *
  38. ***************************************************************************}
  39.  
  40. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  41. {$M 2048,0,20000}
  42.  
  43. {.$DEFINE Debug}         {Activate for status messages}
  44. {.$DEFINE MeasureStack}  {Activate to measure stack usage}
  45.  
  46. program MarkNet;
  47.  
  48. uses
  49.   Dos,
  50.   MemU,
  51.   Xms,
  52.   Ems;
  53.  
  54. const
  55.   MarkFOpen : Boolean = False;    {True while mark file is open}
  56.   Quiet : Boolean = False;        {Set True to avoid screen output}
  57.  
  58. var
  59.   MarkName : PathStr;             {Name of mark file}
  60.  
  61.   DevicePtr : ^DeviceHeader;      {Pointer to the next device header}
  62.   DeviceSegment : Word;           {Current device segment}
  63.   DeviceOffset : Word;            {Current device offset}
  64.   MarkF : file;                   {Dump file}
  65.   DosPtr : ^DosRec;               {Pointer to internal DOS table}
  66.   HiMemSeg : Word;
  67.   CommandSeg : Word;              {PSP segment of primary COMMAND.COM}
  68.   CommandPsp : array[1..$100] of Byte;
  69.   FileTableA : array[1..5] of SftRecPtr;
  70.   FileTableCnt : Word;
  71.   FileRecSize : Word;
  72.   EHandles : Word;                {For tracking EMS allocation}
  73.   EmsPages : ^PageArray;
  74.   XHandles : Word;                {For tracking XMS allocation}
  75.   XmsPages : XmsHandlesPtr;
  76.   HMAStatus : Byte;
  77.   McbG : McbGroup;                {Mcbs allocated as we go resident}
  78.   CDCnt : Word;                   {For tracking MSCDEX information}
  79.   CDInfo : CDROMDeviceArray;
  80.  
  81.   SaveExit : Pointer;
  82.  
  83.   {$IFDEF MeasureStack}
  84.   I : Word;
  85.   {$ENDIF}
  86.  
  87.   procedure ExitHandler; far;
  88.     {-Trap error exits (only)}
  89.   begin
  90.     ExitProc := SaveExit;
  91.     if MarkFOpen then begin
  92.       if IoResult = 0 then ;
  93.       Close(MarkF);
  94.       if IoResult = 0 then ;
  95.       Erase(MarkF);
  96.     end;
  97.     {Turbo will swap back, so undo what we've done already}
  98.     SwapVectors;
  99.   end;
  100.  
  101.   procedure Abort(Msg : String);
  102.     {-Halt in case of error}
  103.   begin
  104.     WriteLn(Msg);
  105.     Halt(1);
  106.   end;
  107.  
  108.   procedure FindDevChain;
  109.     {-Return segment, offset and pointer to NUL device}
  110.   begin
  111.     DosPtr := Ptr(OS(DosList).S, OS(DosList).O-2);
  112.     DevicePtr := @DosPtr^.NullDevice;
  113.     DeviceSegment := OS(DevicePtr).S;
  114.     DeviceOffset := OS(DevicePtr).O;
  115.   end;
  116.  
  117.   procedure CheckWriteError;
  118.     {-Check for errors writing to mark file}
  119.   begin
  120.     if IoResult = 0 then
  121.       Exit;
  122.     Abort('Error writing to '+MarkName);
  123.   end;
  124.  
  125.   procedure SaveStandardInfo;
  126.     {-Save the ID string, the vectors, and so on}
  127.   type
  128.     IDArray = array[1..4] of Char;
  129.   var
  130.     PSeg : Word;
  131.     ID : IDArray;
  132.   begin
  133.     {Write the ID string}
  134.     {$IFDEF Debug}
  135.     WriteLn('Writing mark file ID string');
  136.     {$ENDIF}
  137.     ID := NetMarkID;
  138.     BlockWrite(MarkF, ID, SizeOf(IDArray));
  139.     CheckWriteError;
  140.  
  141.     {Write the start address of the device chain}
  142.     {$IFDEF Debug}
  143.     WriteLn('Writing null device address');
  144.     {$ENDIF}
  145.     BlockWrite(MarkF, DevicePtr, SizeOf(Pointer));
  146.     CheckWriteError;
  147.  
  148.     {Write the vector table}
  149.     {$IFDEF Debug}
  150.     WriteLn('Writing interrupt vector table');
  151.     {$ENDIF}
  152.     BlockWrite(MarkF, Mem[0:0], 1024);
  153.     CheckWriteError;
  154.  
  155.     {Write miscellaneous save areas}
  156.     {$IFDEF Debug}
  157.     WriteLn('Writing EGA save table');
  158.     {$ENDIF}
  159.     BlockWrite(MarkF, Mem[$40:$A8], 8); {EGA save table}
  160.     CheckWriteError;
  161.     {$IFDEF Debug}
  162.     WriteLn('Writing interapplications communication area');
  163.     {$ENDIF}
  164.     BlockWrite(MarkF, Mem[$40:$F0], 16); {Interapplications communication area}
  165.     CheckWriteError;
  166.     {$IFDEF Debug}
  167.     WriteLn('Writing parent PSP segment and length');
  168.     {$ENDIF}
  169.     PSeg := Mem[PrefixSeg:$16];
  170.     BlockWrite(MarkF, PSeg, 2); {Parent's PSP segment}
  171.     BlockWrite(MarkF, Mem[PSeg-1:3], 2); {Parent's PSP's length}
  172.     CheckWriteError;
  173.     {$IFDEF Debug}
  174.     WriteLn('Writing BIOS printer table');
  175.     {$ENDIF}
  176.     BlockWrite(MarkF, Mem[$40:$0], 18); {Com ports, Printer ports, Equip flag}
  177.     CheckWriteError;
  178.  
  179.     {Write EMS information}
  180.     if EMSpresent then begin
  181.       if MaxAvail < 2048 then begin
  182.         WriteLn('Need 2048 bytes for EMS handle table. Have ', MaxAvail);
  183.         Abort('Insufficient memory');
  184.       end;
  185.       GetMem(EmsPages, 2048);
  186.       EHandles := EMSHandles(EmsPages^);
  187.     end else
  188.       EHandles := 0;
  189.     {$IFDEF Debug}
  190.     WriteLn('Writing EMS handle information');
  191.     {$ENDIF}
  192.     BlockWrite(MarkF, EHandles, SizeOf(Word));
  193.     if EHandles <> 0 then
  194.       BlockWrite(MarkF, EmsPages^, SizeOf(HandlePageRecord)*EHandles);
  195.     CheckWriteError;
  196.  
  197.     {Write XMS information}
  198.     if XmsInstalled then begin
  199.       XHandles := GetXmsHandles(XmsPages);
  200.       HMAStatus := AllocateHma($FFFF);
  201.       if HMAStatus = 0 then
  202.         if FreeHma = 0 then ;
  203.     end else begin
  204.       XHandles := 0;
  205.       HMAStatus := $80;
  206.     end;
  207.     {$IFDEF Debug}
  208.     WriteLn('Writing XMS handle and HMA information');
  209.     {$ENDIF}
  210.     BlockWrite(MarkF, XHandles, SizeOf(Word));
  211.     if XHandles <> 0 then
  212.       BlockWrite(MarkF, XmsPages^, SizeOf(XmsHandleRecord)*XHandles);
  213.     BlockWrite(MarkF, HMAStatus, SizeOf(Byte));
  214.     CheckWriteError;
  215.   end;
  216.  
  217.   procedure SaveDevChain;
  218.     {-Save the device driver chain}
  219.   begin
  220.     {$IFDEF Debug}
  221.     WriteLn('Saving device driver chain');
  222.     {$ENDIF}
  223.     while OS(DevicePtr).O <> $FFFF do begin
  224.       BlockWrite(MarkF, DevicePtr^, SizeOf(DeviceHeader));
  225.       CheckWriteError;
  226.       with DevicePtr^ do
  227.         DevicePtr := Ptr(NextHeaderSegment, NextHeaderOffset);
  228.     end;
  229.   end;
  230.  
  231.   procedure BufferFileTable;
  232.     {-Save an image of the system file table}
  233.   var
  234.     S : SftRecPtr;
  235.     Size : Word;
  236.   begin
  237.     with DosPtr^ do begin
  238.       S := FirstSFT;
  239.       FileTableCnt := 0;
  240.       while OS(S).O <> $FFFF do begin
  241.         Inc(FileTableCnt);
  242.         Size := 6+S^.Count*FileRecSize;
  243.         if MaxAvail < Size then begin
  244.           WriteLn('Need ', Size, ' bytes for system file table. Have ', MaxAvail);
  245.           Abort('Insufficient memory');
  246.         end;
  247.         GetMem(FileTableA[FileTableCnt], Size);
  248.         Move(S^, FileTableA[FileTableCnt]^, Size);
  249.         S := S^.Next;
  250.       end;
  251.     end;
  252.   end;
  253.  
  254.   procedure BufferAllocatedMcbs;
  255.     {-Save an array of all allocated Mcbs}
  256.   var
  257.     M : McbPtr;
  258.  
  259.     procedure AddMcbs;
  260.     var
  261.       Done : Boolean;
  262.     begin
  263.         repeat
  264.           inc(McbG.Count);
  265.           with McbG.Mcbs[McbG.Count] do begin
  266.             mcb := OS(M).S;
  267.             psp := M^.Psp;
  268.           end;
  269.           Done := (M^.Id = 'Z');
  270.           M := Ptr(OS(M).S+M^.Len+1, 0);
  271.         until Done;
  272.     end;
  273.  
  274.   begin
  275.     McbG.Count := 0;
  276.     M := Mcb1;
  277.     AddMcbs;
  278.  
  279.     if HiMemSeg <> 0 then begin
  280.       M := Ptr(HiMemSeg, 0);
  281.       AddMcbs;
  282.     end;
  283.   end;
  284.  
  285.   procedure SaveDOSTable;
  286.     {-Save the DOS internal variables table}
  287.   var
  288.     DosBase : Pointer;
  289.     Size : Word;
  290.   begin
  291.     {$IFDEF Debug}
  292.     WriteLn('Saving DOS data area at 0050:0000');
  293.     {$ENDIF}
  294.     BlockWrite(MarkF, mem[$50:$0], $200);
  295.     CheckWriteError;
  296.     DosBase := Ptr(OS(DosPtr).S, 0);
  297.     Size := OS(DosPtr^.FirstSFT).O;
  298.     {$IFDEF Debug}
  299.     WriteLn('Saving DOS variables table at ', HexPtr(DosBase));
  300.     {$ENDIF}
  301.     BlockWrite(MarkF, Size, SizeOf(Word));
  302.     BlockWrite(MarkF, DosBase^, Size);
  303.     CheckWriteError;
  304.   end;
  305.  
  306.   procedure SaveFileTable;
  307.     {-Save the state of the file table}
  308.   var
  309.     I : Word;
  310.     Size : Word;
  311.   begin
  312.     {$IFDEF Debug}
  313.     WriteLn('Saving DOS file table at ', HexPtr(DosPtr^.FirstSFT));
  314.     {$ENDIF}
  315.     BlockWrite(MarkF, FileTableCnt, SizeOf(Word));
  316.     for I := 1 to FileTableCnt do begin
  317.       Size := 6+FileTableA[I]^.Count*FileRecSize;
  318.       BlockWrite(MarkF, FileTableA[I]^, Size);
  319.     end;
  320.     CheckWriteError;
  321.   end;
  322.  
  323.   procedure BufferCommandPSP;
  324.     {-Save the PSP of COMMAND.COM}
  325.   var
  326.     PspPtr : Pointer;
  327.   begin
  328.     CommandSeg := MasterCommandSeg(HiMemSeg);
  329.     PspPtr := Ptr(CommandSeg, 0);
  330.     Move(PspPtr^, CommandPsp, $100);
  331.   end;
  332.  
  333.   procedure SaveCommandPSP;
  334.   begin
  335.     {$IFDEF Debug}
  336.     WriteLn('Saving COMMAND.COM PSP at ', HexW(CommandSeg), ':0000');
  337.     {$ENDIF}
  338.     BlockWrite(MarkF, CommandPsp, $100);
  339.     CheckWriteError;
  340.   end;
  341.  
  342.   procedure SaveCommandPatch;
  343.     {-Save the patch that NetWare applies to command.com}
  344.   label
  345.     ExitPoint;
  346.   const
  347.     Patch : array[0..14] of Char = ':/'#0'_______.___'#0;
  348.   var
  349.     Segm : Word;
  350.     Ofst : Word;
  351.     Indx : Word;
  352.   begin
  353. (*
  354.     for Segm := CommandSeg to PrefixSeg do
  355.       for Ofst := 0 to 15 do begin
  356.         Indx := 0;
  357.         while (Indx <= 14) and (Patch[Indx] = Char(Mem[Segm:Ofst+Indx])) do
  358.           Inc(Indx);
  359.         if Indx > 14 then begin
  360.           {$IFDEF Debug}
  361.           WriteLn('Saving COMMAND patch address at ', HexW(Segm), ':', HexW(Ofst));
  362.           {$ENDIF}
  363.           goto ExitPoint;
  364.         end;
  365.       end;
  366. *)
  367.     Segm := 0;
  368.     Ofst := 0;
  369. ExitPoint:
  370.     BlockWrite(MarkF, Ofst, SizeOf(Word));
  371.     BlockWrite(MarkF, Segm, SizeOf(Word));
  372.     CheckWriteError;
  373.   end;
  374.  
  375.   procedure FindEnv(CommandSeg : Word; var EnvSeg, EnvLen : Word);
  376.     {-Return the segment and length of the master environment}
  377.   var
  378.     Mcb : Word;
  379.   begin
  380.     Mcb := CommandSeg-1;
  381.     EnvSeg := MemW[CommandSeg:$2C];
  382.     if EnvSeg = 0 then
  383.       {Master environment is next block past COMMAND}
  384.       EnvSeg := Commandseg+MemW[Mcb:3]+1;
  385.     EnvLen := MemW[(EnvSeg-1):3] shl 4;
  386.   end;
  387.  
  388.   procedure SaveDosEnvironment;
  389.     {-Save the master copy of the DOS environment}
  390.   var
  391.     EnvSeg : Word;
  392.     EnvLen : Word;
  393.     P : Pointer;
  394.   begin
  395.     FindEnv(CommandSeg, EnvSeg, EnvLen);
  396.     {$IFDEF Debug}
  397.     WriteLn('Saving master environment, ', EnvLen, ' bytes at ', HexW(EnvSeg), ':0000');
  398.     {$ENDIF}
  399.     P := Ptr(EnvSeg, 0);
  400.     BlockWrite(MarkF, EnvLen, SizeOf(Word));
  401.     BlockWrite(MarkF, P^, EnvLen);
  402.     CheckWriteError;
  403.   end;
  404.  
  405.   procedure SaveCommState;
  406.     {-Save the state of the communications controllers}
  407.   var
  408.     PicMask : Byte;
  409.     Com : Byte;
  410.     LCRSave : Byte;
  411.     Base : Word;
  412.     ComPortBase : array[1..2] of Word absolute $40:0; {Com port base addresses}
  413.  
  414.     procedure SaveReg(Offset : Byte);
  415.       {-Save one communications register}
  416.     var
  417.       Reg : Byte;
  418.     begin
  419.       Reg := Port[Base+Offset];
  420.       BlockWrite(MarkF, Reg, SizeOf(Byte));
  421.       CheckWriteError;
  422.     end;
  423.  
  424.   begin
  425.     {$IFDEF Debug}
  426.     WriteLn('Saving communications environment');
  427.     {$ENDIF}
  428.  
  429.     {Save the 8259 interrupt enable mask}
  430.     PicMask := Port[$21];
  431.     BlockWrite(MarkF, PicMask, SizeOf(Byte));
  432.     CheckWriteError;
  433.  
  434.     for Com := 1 to 2 do begin
  435.       Base := ComPortBase[Com];
  436.  
  437.       {Save the Com port base address}
  438.       BlockWrite(MarkF, Base, SizeOf(Word));
  439.       CheckWriteError;
  440.  
  441.       if Base <> 0 then begin
  442.         {Save the rest of the control state}
  443.         SaveReg(IER);             {Interrupt enable register}
  444.         SaveReg(LCR);             {Line control register}
  445.         SaveReg(MCR);             {Modem control register}
  446.         LCRSave := Port[Base+LCR]; {Save line control register}
  447.         Port[Base+LCR] := LCRSave or $80; {Enable baud rate divisor registers}
  448.         SaveReg(BRL);             {Baud rate divisor low}
  449.         SaveReg(BRH);             {Baud rate divisor high}
  450.         Port[Base+LCR] := LCRSave; {Restore line control register}
  451.       end;
  452.     end;
  453.   end;
  454.  
  455.   procedure SaveCDRoms;
  456.     {-Save list of CD-ROM devices}
  457.   begin
  458.     {$IFDEF Debug}
  459.     WriteLn('Saving CD-ROM information');
  460.     {$ENDIF}
  461.     CDCnt := GetCDCount(CDInfo);
  462.     BlockWrite(MarkF, CDCnt, SizeOf(Word));
  463.     CheckWriteError;
  464.     if CDCnt <> 0 then begin
  465.       BlockWrite(MarkF, CDInfo, CDCnt*SizeOf(CDROMDeviceRec));
  466.       CheckWriteError;
  467.     end;
  468.   end;
  469.  
  470.   procedure SaveAllocatedMcbs;
  471.     {-Save list of allocated memory control blocks}
  472.   begin
  473.     {$IFDEF Debug}
  474.     WriteLn('Saving memory allocation group');
  475.     {$ENDIF}
  476.     {Save the number of Mcbs}
  477.     BlockWrite(MarkF, McbG.Count, SizeOf(Word));
  478.     CheckWriteError;
  479.     {Save the used Mcbs}
  480.     BlockWrite(MarkF, McbG.Mcbs, 2*SizeOf(Word)*McbG.Count);
  481.     CheckWriteError;
  482.   end;
  483.  
  484.   function CompaqDOS30 : Boolean; assembler;
  485.     {-Return true if Compaq DOS 3.0}
  486.   asm
  487.     mov ah,$34
  488.     int $21
  489.     cmp bx,$019C
  490.     mov al,1
  491.     jz @Done
  492.     dec al
  493. @Done:
  494.   end;
  495.  
  496.   procedure ValidateDosVersion;
  497.     {-Assure supported version of DOS and compute size of DOS internal filerec}
  498.   var
  499.     DosVer : Word;
  500.   begin
  501.     DosVer := DosVersion;
  502.     case Hi(DosVer) of
  503.       3 : if (Hi(DosVer) < $0A) and not CompaqDOS30 then
  504.             {IBM DOS 3.0}
  505.             FileRecSize := 56
  506.           else
  507.             {DOS 3.1+ or Compaq DOS 3.0}
  508.             FileRecSize := 53;
  509.       4, 5, 6 : FileRecSize := 59;
  510.     else
  511.       Abort('Requires DOS 3 - 6');
  512.     end;
  513.   end;
  514.  
  515.   procedure SaveIDStrings;
  516.     {-Save identification strings within the PSP}
  517.   var
  518.     ID : String[10];
  519.   begin
  520.     Move(MarkName, Mem[PrefixSeg:$80], Length(MarkName)+1);
  521.     Mem[PrefixSeg:$80+Length(MarkName)+1] := 13;
  522.     ID := NmarkID;
  523.     Move(ID[1], Mem[PrefixSeg:NmarkOffset], Length(ID));
  524.   end;
  525.  
  526.   procedure CloseStandardFiles;
  527.     {-Close all standard files}
  528.   var
  529.     H : Word;
  530.   begin
  531.     for H := 0 to 4 do
  532.       asm
  533.         mov ah,$3E
  534.         mov bx,H
  535.         int $21
  536.       end;
  537.   end;
  538.  
  539.   procedure GetOptions;
  540.     {-Get command line options}
  541.   var
  542.     Arg : String[127];
  543.  
  544.     procedure UnknownOption;
  545.     begin
  546.       WriteLn('Unknown command line option: ', Arg);
  547.       Halt(1);
  548.     end;
  549.  
  550.     procedure BadOption;
  551.     begin
  552.       WriteLn('Invalid command line option: ', Arg);
  553.       Halt(1);
  554.     end;
  555.  
  556.     procedure WriteCopyright;
  557.     begin
  558.       WriteLn('MARKNET ', Version, ', Copyright 1993 TurboPower Software');
  559.     end;
  560.  
  561.     procedure WriteHelp;
  562.     begin
  563.       WriteCopyright;
  564.       WriteLn;
  565.       WriteLn('MARKNET saves a picture of the PC system status in a file,');
  566.       WriteLn('so that the state can later be restored by using RELNET.');
  567.       WriteLn;
  568.       WriteLn('MARKNET accepts the following command line syntax:');
  569.       WriteLn;
  570.       WriteLn('  MARKNET [Options] MarkFile');
  571.       WriteLn;
  572.       WriteLn('Options may be preceded by either / or -. Valid options are as follows:');
  573.       WriteLn('     /Q     write no screen output.');
  574.       WriteLn('     /?     write this help screen.');
  575.       Halt(1);
  576.     end;
  577.  
  578.     procedure GetArgs(S : String);
  579.     var
  580.       SPos : Word;
  581.     begin
  582.       SPos := 1;
  583.       repeat
  584.         Arg := NextArg(S, SPos);
  585.         if Arg = '' then
  586.           Exit;
  587.         if Arg = '?' then
  588.           WriteHelp
  589.         else
  590.           case Arg[1] of
  591.             '-', '/' :
  592.               case Length(Arg) of
  593.                 1 : BadOption;
  594.                 2 : case Upcase(Arg[2]) of
  595.                       '?' : WriteHelp;
  596.                       'Q' : Quiet := True;
  597.                     else
  598.                       BadOption;
  599.                     end;
  600.               else
  601.                 UnknownOption;
  602.               end;
  603.           else
  604.             if Length(MarkName) <> 0 then
  605.               BadOption
  606.             else
  607.               MarkName := StUpcase(Arg);
  608.           end;
  609.       until False;
  610.     end;
  611.  
  612.   begin
  613.     MarkName := '';
  614.  
  615.     {Get arguments from the command line and the environment}
  616.     GetArgs(StringPtr(Ptr(PrefixSeg, $80))^);
  617.     GetArgs(GetEnv('MARKNET'));
  618.  
  619.     {Assure mark file specified}
  620.     if Length(MarkName) = 0 then
  621.       WriteHelp;
  622.     if not Quiet then
  623.       WriteCopyright;
  624.   end;
  625.  
  626. begin
  627.   {$IFDEF MeasureStack}
  628.   fillchar(mem[sseg:0], sptr-16, $AA);
  629.   {$ENDIF}
  630.  
  631.   {Must run with standard DOS vectors}
  632.   SwapVectors;
  633.   SaveExit := ExitProc;
  634.   ExitProc := @ExitHandler;
  635.  
  636.   {Get command line options}
  637.   GetOptions;
  638.  
  639.   {Assure supported version of DOS}
  640.   ValidateDosVersion;
  641.  
  642.   {Find the device driver chain and the DOS internal table}
  643.   FindDevChain;
  644.  
  645.   {Find first block of high memory}
  646.   HiMemSeg := FindHiMemStart;
  647.  
  648.   {Save PSP region of COMMAND.COM}
  649.   BufferCommandPSP;
  650.  
  651.   {Buffer the DOS file table}
  652.   BufferFileTable;
  653.  
  654.   {Deallocate environment}
  655.   asm
  656.     mov es,PrefixSeg
  657.     mov es,es:[$002C]
  658.     mov ah,$49
  659.     int $21
  660.   end;
  661.  
  662.   {Buffer the allocated mcb array}
  663.   BufferAllocatedMcbs;
  664.  
  665.   {Open the mark file}
  666.   Assign(MarkF, MarkName);
  667.   Rewrite(MarkF, 1);
  668.   if IoResult <> 0 then
  669.     Abort('Error creating '+MarkName);
  670.   MarkFOpen := True;
  671.  
  672.   {Save ID string, interrupt vectors and other standard state information}
  673.   SaveStandardInfo;
  674.  
  675.   {Save the device driver chain}
  676.   SaveDevChain;
  677.  
  678.   {Save the DOS internal variables table}
  679.   SaveDOSTable;
  680.  
  681.   {Save the DOS internal file management table}
  682.   SaveFileTable;
  683.  
  684.   {Save the PSP of COMMAND.COM}
  685.   SaveCommandPSP;
  686.  
  687.   {Save the location that NetWare may patch in COMMAND.COM}
  688.   SaveCommandPatch;
  689.  
  690.   {Save the master copy of the DOS environment}
  691.   SaveDosEnvironment;
  692.  
  693.   {Save the state of the communications controllers}
  694.   SaveCommState;
  695.  
  696.   {Save list of CD-ROM devices}
  697.   SaveCDRoms;
  698.  
  699.   {Save list of allocated memory control blocks}
  700.   SaveAllocatedMcbs;
  701.  
  702.   {Close mark file}
  703.   Close(MarkF);
  704.   CheckWriteError;
  705.  
  706.   {Move ID strings into place}
  707.   SaveIDStrings;
  708.  
  709.   if not Quiet then
  710.     WriteLn('Stored mark information in ', MarkName);
  711.  
  712.   {$IFDEF MeasureStack}
  713.   I := 0;
  714.   while I < SPtr-16 do
  715.     if mem[sseg:i] <> $AA then begin
  716.       writeln('unused stack ', i, ' bytes');
  717.       I := SPtr;
  718.     end else
  719.       inc(I);
  720.   {$ENDIF}
  721.  
  722.   Flush(Output);
  723.  
  724.   {Close file handles}
  725.   CloseStandardFiles;
  726.  
  727.   {Go resident}
  728.   asm
  729.     mov dl,byte ptr markname
  730.     xor dh,dh
  731.     add dx,$0090
  732.     mov cl,4
  733.     shr dx,cl
  734.     mov ax,$3100
  735.     int $21
  736.   end;
  737. end.
  738.